package binding

import (
	"mime/multipart"
	"sort"
	"strings"
	"time"

	"gitee.com/go-errors/errors"
	"github.com/valyala/fastjson/fastfloat"
)

func formSource(v map[string][]string) Source {
	return formValuer(v)
}

type formValuer map[string][]string

func (ver formValuer) Get(key string) (v Source, err error) {
	fv := formValuer{}
	for name, values := range ver {
		if strings.HasPrefix(name, key) {
			if sub := name[len(key):]; len(sub) == 0 || sub[0] == '.' || sub[0] == '[' {
				if len(sub) > 0 && sub[0] == '.' {
					sub = sub[1:]
				}
				fv[sub] = append(fv[sub], values...)
			}
		}
	}

	if len(fv) == 0 {
		err = ErrNoValue
	} else {
		v = fv
	}
	return
}

func (ver formValuer) Map(iter func(key string, valuer Source)) {
	if len(ver) == 0 {
		return
	}
	var objects = map[string]formValuer{}
	for name, values := range ver {
		if len(name) > 0 && name[0] == '[' {
			//是数组
			continue
		}

		if dot := strings.Index(name, ".["); dot != -1 {
			key := name[:dot]
			subKey := name[dot:]

			if _, find := objects[key]; !find {
				objects[key] = formValuer{}
			}

			if subKey[0] == '.' {
				objects[key][subKey[1:]] = append(objects[key][subKey[1:]], values...)
			} else {
				objects[key][subKey] = append(objects[key][subKey], values...)
			}
			continue
		}

		if _, find := objects[name]; !find {
			objects[name] = formValuer{}
		}
		objects[name][""] = append(objects[name][""], values...)
	}

	for key, v := range objects {
		iter(key, v)
	}
	return
}

func (ver formValuer) Array(iter func(index int, valuer Source) bool, beforeFs ...func(size int)) {
	var array = map[int]formValuer{}
	for name, values := range ver {
		if len(name) > 0 && name[0] != '[' {
			//不是数组
			continue
		}

		if dot := strings.Index(name, "]"); dot != -1 {
			key := strings.Trim(name[:dot+1], "[]")
			if key == "" {
				key = "0"
			}

			key64, err := fastfloat.ParseInt64(key)
			if err != nil {
				continue
			}

			ik := int(key64)
			subKey := name[dot+1:]
			if _, find := array[ik]; !find {
				array[ik] = formValuer{}
			}

			if len(subKey) > 0 && subKey[0] == '.' {
				array[ik][subKey[1:]] = append(array[ik][subKey[1:]], values...)
			} else {
				array[ik][subKey] = append(array[ik][subKey], values...)
			}
		}
	}

	l := len(array)
	for _, before := range beforeFs {
		before(l)
	}

	var keys []int
	for key := range array {
		keys = append(keys, key)
	}
	sort.Ints(keys)
	for i, key := range keys {
		if !iter(i, array[key]) {
			break
		}
	}
	return
}

func (ver formValuer) String() (s string, err error) {
	if len(ver) == 0 {
		err = ErrNoValue
	} else if ss := ver[""]; len(ss) == 0 {
		err = ErrNoValue
	} else {
		s = ss[0]
	}
	return
}

func (ver formValuer) Int() (i int64, err error) {
	var s string
	if s, err = ver.String(); err != nil {
		return
	}
	return fastfloat.ParseInt64(s)
}

func (ver formValuer) Uint() (i uint64, err error) {
	var s string
	if s, err = ver.String(); err != nil {
		return
	}
	return fastfloat.ParseUint64(s)
}

func (ver formValuer) Float() (f float64, err error) {
	var s string
	if s, err = ver.String(); err != nil {
		return
	}
	return fastfloat.Parse(s)
}

func (ver formValuer) Bool() (b bool, err error) {
	if s, err := ver.String(); err != nil {
		return false, err
	} else {
		return parseBool(s)
	}
}

func (ver formValuer) Time() (t time.Time, err error) {
	var s string
	if s, err = ver.String(); err != nil {
		return
	}
	var ex error
	for _, layout := range timeLayouts {
		if t, ex = time.Parse(layout, s); ex != nil {
			err = errors.Multi(err, ex)
		} else {
			err = nil
			return
		}
	}
	return
}

func (ver formValuer) Duration() (d time.Duration, err error) {
	if s, err := ver.String(); err != nil {
		return d, err
	} else {
		return time.ParseDuration(s)
	}
}

func (ver formValuer) File() (f *multipart.FileHeader, err error) {
	return nil, ErrNoValue
}

func (ver formValuer) Files() (f []*multipart.FileHeader, err error) {
	return nil, ErrNoValue
}

func (ver formValuer) getKey(name string) (string, error) {
	if len(name) > 0 && name[0] == '.' {
		name = name[1:]
	}
	if name == "" {
		return name, nil
	}

	if name[0] == '[' {
		if idx := strings.IndexAny(name, "]"); idx != -1 {
			return name[:idx+1], nil
		}
		return "", errors.New("tag '[]' not closed")
	}

	if idx := strings.IndexAny(name, "."); idx != -1 {
		return name[:idx], nil
	}
	return name, nil
}

var _ Source = formValuer{}
